perm filename FREEFO.LES[UP,DOC]2 blob sn#029838 filedate 1973-03-21 generic text, type T, neo UTF8
BEGIN  "FREEFOROL"
	COMMENT  10 MARCH 1973

OPERATION

Freeforol is a text macro processor that can be used to generate form
letters  and other fill-in-the-blanks text.  If you say  R FREEFO, it
types a "*" and expects a keyboard input of the form
    <source file list>
or
    <destination file>←<source file list>
where the source file  list  consists  of  one  or  more  file  names
separated  by  a single "&", space, or tab.  These must be text files
and will be effectively concatenated in the order given.

The output goes to the destination file,  if  given.   Otherwise  one
copy of the output is spooled.

PROCESS

The source file(s) are expected to contain a text macro  followed  by
one or more argument lists.  This program outputs a copy of the macro
for each arguments list, with arguments substituted.

The first character  of  the  source  file  is  taken  as  the  macro
delimiter.     The macro begins just after this.  Each place where an
argument   is   to   be    substituted    is    designated    by    a
<delimiter><parameter> pair, where the <parameter> may be "1" through
"9" (representing the first through ninth arguments) or  "A"  through
"J" (10th through 20th arguments).   The macro ends with
    <delimiter><separator><delimeter>
where the <separator> is any non-<parameter>.

The argument lists begin on the next line, which can of course be  in
a  different file if you wish.  Either of two formats may be used, as
follows.

1) If the <separator> is <CR> or <CR><LF>, then each line of text  is
interpreted  as  an  argument  and  the list is terminated by a blank
line.   This format is convenient for lists of addressees that are to
be inserted in a form letter.

2) If the separator is any other character, then each line of text is
interpreted  as  a  list  whose  arguments  are  separated   by   the
<separator>.

Thus, the general form of the source file(s) is
	<delimiter>
	<macro body, including parameters>
	<delimiter><separator><delimiter>
	<argument lists>

EXAMPLES

For example, suppose the following source file were used:
_____________________________________________________________________
⊗		      Veterans Administration
                          Washington, D. C.
						          4 July 1984
⊗1 ⊗2
⊗3
⊗4

Dear ⊗1:

Our office has  recently  determined  that  you  qualify  for  a  10%
disability.   Congratulations ...

⊗
⊗
John
Brown
Arlington National Cemetery
Arlington, Virginia

Speedy
Gonzales
Blue Fox Restaurant
Tijuana, Mexico

_____________________________________________________________________

Then "⊗" is the delimiter and the separator  is  <CR><LF>,  so  there
would  be  two  copies  of  the  letter generated, with the first one
addressed:
John Brown
Arlington National Cemetery
Arlington, Virginia

Dear John:
....

Alternatively, if the source file were:
_____________________________________________________________________
@
	This is a@2 macro processor,
	but it is @1.

@,@
fairly general, simple
rather easy to understand,n elegant
_____________________________________________________________________
then "@" is the delimiter and "," is the separator, so this produces:
	This is a simple macro processor,
	but it is fairly general.

	This is an elegant macro processor,
	but it is fairly easy to understand.

FEATURES

Several features should be noted.
1)  Any form feeds in the macro body are preserved, but they are
    ignored in the argument lists.
2)  Excess or unused arguments are ignored, so you can put comments
    in the argument lists that will not show up in the output.
3)  Arguments that are omitted are automatically set to NULL.
;

DEFINE NOVA="25";  COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS;
DEFINE ARGS="20";  COMMENT THE MAXIMUM NUMBER OF AGRUMENTS;

DEFINE THIS="COMMENT", CR="'15", LF="'12", HT="'11", FF="'14",↓="'15&'12",
	THRU="STEP 1 UNTIL", ⊃="←1 STEP 1 UNTIL", LN="LENGTH",
	ERR(MES)="BEGIN OUTSTR(""MES""&↓); CALL(0,""EXIT"") END";
DEFINE INCH="1", OUCH="2", LINE="1", MARK="2", CHAR="3",AMP="4";

REQUIRE 2000 STRING_SPACE;
STRING ARRAY TEXT[1:NOVA];  THIS HOLDS SEGMENTS OF MACRO TEXT;
INTEGER ARRAY PARG[1:NOVA];
STRING ARRAY VAL[1:ARGS];  THIS HOLDS CURRENT ARGUMENT VALUES;
STRING S,S0,SOURCE,DEST;
INTEGER BRK, EOF,T, V, I,FLAG;

EXTERNAL PROCEDURE SPOOL(STRING FILE; INTEGER IOCHAN,FLAGS);

PROCEDURE LOOK;  BEGIN  BOOLEAN FL;  STRING FILE;
	LOOKUP(INCH,FILE←SCAN(SOURCE,AMP,BRK),FL);
	IF FL THEN BEGIN OUTSTR(FILE&" not found"&↓); CALL(0,"EXIT") END;
	END;

STRING SIMPLE PROCEDURE READ;	BEGIN  THIS READS THE SOURCE FILE(S);
	STRING RS;	RS←INPUT(INCH,CHAR);
	IF ¬EOF ∨ LN(SOURCE)=0 THEN RETURN(RS);
	CLOSE(INCH);  LOOK;	RETURN(RS&INPUT(INCH,CHAR))
	END;

	THIS INITIALIZES I/O;
OUTSTR("*");
OPEN(INCH,"DSK",1,2,0,4000,BRK,EOF);  OPEN(OUCH,"DSK",1,0,2,0,BRK,EOF);
SETBREAK(AMP,"←",NULL,"INS");
DEST←SCAN(SOURCE←INCHWL,AMP,BRK);	THIS READS THE FILE NAMES;
IF BRK="←" THEN BEGIN "destination file"
	ENTER(OUCH,DEST,FLAG);	DEST←NULL;
	END
    ELSE BEGIN "spool it"
	SOURCE←DEST; THIS RESTORES SOURCE FILE NAMES;
	DEST←"FREEF";  I←"0"-1;
	DO BEGIN LOOKUP(OUCH,DEST&(I←I+1)&".TMP",FLAG);  CLOSE(OUCH) END
	    UNTIL FLAG;
	ENTER(OUCH,DEST←DEST&I&".TMP",FLAG);
	END;
IF FLAG THEN ERR(Destination file cannot be written);
BREAKSET(AMP," &"&HT,"I");  LOOK;

	THIS READS AND SEGMENTS THE TEXT MACRO;
SETBREAK(CHAR,NULL,NULL,"XNA");  SETBREAK(CHAR,READ,NULL,"INS");
S0←READ;  T←0;
DO V←LOP(TEXT[T←T+1]←READ) UNTIL (I←PARG[T]←V-(IF "0"<V≤"9" THEN "0"
    ELSE IF "A"≤V THEN "A"-10 ELSE "A"))≤0 ∨ I>ARGS;

	THIS FINDS THE SEPARATORS AND TERMINATOR;
SETBREAK(MARK,V,NULL,"INS");  SETBREAK(CHAR,LF,CR&FF,"INS");
I←READ;		THIS FLUSHES THE REST OF THE LINE;

DO BEGIN	THIS SCANS ARGUMENT LISTS AND OUTPUTS TEXT;
	IF V=CR THEN BEGIN "1 ARGUMENT PER LINE"
		FOR I ⊃ ARGS DO IF LN(VAL[I]←READ)=0 THEN DONE;
		END
	    ELSE BEGIN "SET OF ARGUMENTS PER LINE"
		S←READ;
		FOR I ⊃ ARGS DO IF LN(VAL[I]←SCAN(S,MARK,BRK))=0 THEN DONE;
		END;
	IF EOF ∧ I=1 THEN DONE;
	FOR I←I THRU ARGS DO VAL[I]←NULL;
	OUT(OUCH,S0);
	FOR I ⊃ T-1 DO BEGIN OUT(OUCH,VAL[PARG[I]]); OUT(OUCH,TEXT[I]) END;
	END UNTIL EOF;
RELEASE(INCH);  RELEASE(OUCH);
IF LN(DEST) THEN SPOOL(OUCH,DEST,1);
END;